Load Datasets


# Error Index: ccc, scott, marriot, trcovw, tracew, friedman, rubin
indices <-c("kl", "ch", "hartigan", "cindex", "db", "silhouette", "duda", "pseudot2", "beale", "ratkowsky", "ball", "ptbiserial", "gap", "frey", "mcclain", "gamma", "gplus", "tau", "dunn", "hubert", "sdindex", "dindex", "sdbw")

cls.features <- select(coin_features, -1)

nc_list <- c()
for (idx in indices){
  nc <- NbClust(cls.features, method="complete", index=idx)$Best.nc # find number of clusters
  nc_list <- c(nc_list, nc[1])
}
Warning in max(res[, 25], na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf
Warning in matrix(c(results), nrow = 2, ncol = 30) :
  data length [59] is not a sub-multiple or multiple of the number of rows [2]
Warning in matrix(c(results), nrow = 2, ncol = 30, dimnames = list(c("Number_clusters",  :
  data length [59] is not a sub-multiple or multiple of the number of rows [2]
Warning in min(k) : no non-missing arguments to min; returning Inf
Warning in max(k) : no non-missing arguments to max; returning -Inf
*** : The Hubert index is a graphical method of determining the number of clusters.
                In the plot of Hubert index, we seek a significant knee that corresponds to a 
                significant increase of the value of the measure i.e the significant peak in Hubert
                index second differences plot. 
 

*** : The D index is a graphical method of determining the number of clusters. 
                In the plot of D index, we seek a significant knee (the significant peak in Dindex
                second differences plot) that corresponds to a significant increase of the value of
                the measure. 
 

stat_mode <- function(v) {
 uniqv <- unique(v)
 uniqv[which.max(tabulate(match(v, uniqv)))]
}

nc <- stat_mode(nc_list)

library(tidyr)

# interpret each cluster
fcluster <- as.data.frame(cls.hclust.cn) 
colnames(fcluster) <- c("cluster")

coin_features.cluster <- bind_cols(list(coin_features, fcluster)) %>% mutate(cluster = as.factor(cluster))
cls.hclust.stats <- bind_cols(list(cls.features, fcluster)) %>% group_by(cluster) %>% summarise_all(list(mean)) %>% gather("traits", "meanval", 2:46) %>% mutate(cluster = as.factor(cluster))

cls.hclust.stats %>% ggplot(aes(x = traits, y = meanval, fill = cluster)) +
  geom_bar(stat="identity", position = "dodge") +
  ggtitle("Average Features by Clusters") +
  xlab("") + 
  ylab("Average Values") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

# filter by each cluster
C1 <- coin_features.cluster %>% filter(cluster == "1")
C2 <- coin_features.cluster %>% filter(cluster == "2")
C3 <- coin_features.cluster %>% filter(cluster == "3")
C4 <- coin_features.cluster %>% filter(cluster == "4")
C5 <- coin_features.cluster %>% filter(cluster == "5")
C6 <- coin_features.cluster %>% filter(cluster == "6")

Pre-COVID19

# visualize coins' prices within cluster
price_cols <- c("Date", "Close", "Volume", "change", "returns", "volatility")
cluster.groups <- c("C1", "C2", "C3", "C4", "C5", "C6")

for (cluster.group in cluster.groups) {
  ndays <- 365
  coins.prices <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.changes <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.returns <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.volatility <- data.frame(matrix(nrow = ndays, ncol = 0))
  for (symbol in get(cluster.group)$symbol) {
    values <- fread(paste("datasets/daily/coins", paste(symbol, 'csv', sep = "."), sep = "/")) %>% as.data.frame() %>% filter(Date >= "2019-01-01" & Date <= "2019-12-31") %>% select(all_of(price_cols)) %>% arrange(Date)
    symbol <- str_split(symbol, "-", simplify = T)[1]
    
    coins.prices['Date'] <- values$Date
    coins.changes['Date'] <- values$Date
    coins.returns['Date'] <- values$Date
    coins.volatility['Date'] <- values$Date
    
    coins.prices[symbol] <- values$Close 
    coins.changes[symbol] <- values$change
    coins.returns[symbol] <- values$returns
    coins.volatility[symbol] <- values$volatility
  }
  
  coins.prices.melt <- reshape2::melt(coins.prices, "Date", value.name = "Prices", variable.name = "Coins")
  coins.prices.plot <- ggplot(data = coins.prices.melt, aes(x = Date, y = Prices, color = Coins)) +
    ggtitle(paste("Price Movement of", cluster.group)) +
    geom_line()
  coins.prices.boxplot <- ggplot(data = coins.prices.melt, aes(x = Coins, y = Prices, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Price Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.prices.plot)
  print(coins.prices.boxplot)
    
  coins.changes.melt <- reshape2::melt(coins.changes, "Date", value.name = "Changes", variable.name = "Coins")
  coins.changes.plot <- ggplot(data = coins.changes.melt, aes(x = Date, y = Changes, color = Coins)) +
    ggtitle(paste("Change Movement of", cluster.group)) +
    geom_line()
  coins.changes.boxplot <- ggplot(data = coins.changes.melt, aes(x = Coins, y = Changes, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Change Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.changes.plot)
  print(coins.changes.boxplot)

  coins.returns.melt <- reshape2::melt(coins.returns, "Date", value.name = "Returns", variable.name = "Coins")
  coins.returns.plot <- ggplot(data = coins.returns.melt, aes(x = Date, y = Returns, color = Coins)) +
    ggtitle(paste("Returns Movement of", cluster.group)) +
    geom_line()
  coins.returns.boxplot <- ggplot(data = coins.returns.melt, aes(x = Coins, y = Returns, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Returns Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.returns.plot)
  print(coins.returns.boxplot)

  coins.volatility.melt <- reshape2::melt(coins.volatility, "Date", value.name = "Volatility", variable.name = "Coins")
  coins.volatility.plot <- ggplot(data = coins.volatility.melt, aes(x = Date, y = Volatility, color = Coins)) +
    ggtitle(paste("Volatility Movement of", cluster.group)) +
    geom_line()
  coins.volatility.boxplot <- ggplot(data = coins.volatility.melt, aes(x = Coins, y = Volatility, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Volatility Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.volatility.plot) 
  print(coins.volatility.boxplot) 

  # average prices for inter-cluster comparison
  coins.prices.avg <- coins.prices.melt %>% group_by(Date) %>% summarise(avg_prices = mean(Prices, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.changes.avg <- coins.changes.melt %>% group_by(Date) %>% summarise(avg_changes = mean(Changes, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.returns.avg <- coins.returns.melt %>% group_by(Date) %>% summarise(avg_returns = mean(Returns, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.volatility.avg <- coins.volatility.melt %>% group_by(Date) %>% summarise(avg_volatility = mean(Volatility, na.rm = T)) %>% mutate(cluster = cluster.group)
  
  assign(paste(cluster.group, "prices.avg", sep = "."), coins.prices.avg)
  assign(paste(cluster.group, "changes.avg", sep = "."), coins.changes.avg)
  assign(paste(cluster.group, "returns.avg", sep = "."), coins.returns.avg)
  assign(paste(cluster.group, "volatility.avg", sep = "."), coins.volatility.avg)
}
Warning: Removed 1760 row(s) containing missing values (geom_path).
Warning: Removed 1760 rows containing non-finite values (stat_boxplot).
notch went outside hinges. Try setting notch=FALSE.
Warning: Removed 1761 row(s) containing missing values (geom_path).
Warning: Removed 1761 rows containing non-finite values (stat_boxplot).
Warning: Removed 1761 row(s) containing missing values (geom_path).
Warning: Removed 1761 rows containing non-finite values (stat_boxplot).
Warning: Removed 1759 row(s) containing missing values (geom_path).
Warning: Removed 1759 rows containing non-finite values (stat_boxplot).
Warning: Removed 3372 row(s) containing missing values (geom_path).
Warning: Removed 3372 rows containing non-finite values (stat_boxplot).
notch went outside hinges. Try setting notch=FALSE.
Warning: Removed 3378 row(s) containing missing values (geom_path).
Warning: Removed 3378 rows containing non-finite values (stat_boxplot).
notch went outside hinges. Try setting notch=FALSE.
Warning: Removed 3378 row(s) containing missing values (geom_path).
Warning: Removed 3378 rows containing non-finite values (stat_boxplot).
notch went outside hinges. Try setting notch=FALSE.
Warning: Removed 3366 row(s) containing missing values (geom_path).
Warning: Removed 3366 rows containing non-finite values (stat_boxplot).
notch went outside hinges. Try setting notch=FALSE.
Warning: Removed 1645 row(s) containing missing values (geom_path).
Warning: Removed 1645 rows containing non-finite values (stat_boxplot).
Warning: Removed 1648 row(s) containing missing values (geom_path).
Warning: Removed 1648 rows containing non-finite values (stat_boxplot).
Warning: Removed 1648 row(s) containing missing values (geom_path).
Warning: Removed 1648 rows containing non-finite values (stat_boxplot).
Warning: Removed 1642 row(s) containing missing values (geom_path).
Warning: Removed 1642 rows containing non-finite values (stat_boxplot).
Warning: Removed 3383 row(s) containing missing values (geom_path).
Warning: Removed 3383 rows containing non-finite values (stat_boxplot).
Warning: Removed 3386 row(s) containing missing values (geom_path).
Warning: Removed 3386 rows containing non-finite values (stat_boxplot).
Warning: Removed 3386 row(s) containing missing values (geom_path).
Warning: Removed 3386 rows containing non-finite values (stat_boxplot).
Warning: Removed 3380 row(s) containing missing values (geom_path).
Warning: Removed 3380 rows containing non-finite values (stat_boxplot).
Warning: Removed 3934 row(s) containing missing values (geom_path).
Warning: Removed 3934 rows containing non-finite values (stat_boxplot).
Warning: Removed 3937 row(s) containing missing values (geom_path).
Warning: Removed 3937 rows containing non-finite values (stat_boxplot).
Warning: Removed 3937 row(s) containing missing values (geom_path).
Warning: Removed 3937 rows containing non-finite values (stat_boxplot).
Warning: Removed 3931 row(s) containing missing values (geom_path).
Warning: Removed 3931 rows containing non-finite values (stat_boxplot).

# inter-cluster prices
for (movement in c("Prices", "Changes", "Returns", "Volatility")) {
  movement.lower <- tolower(movement)
  y_val <- paste("avg", movement.lower, sep = "_")
  cluster.plot <- ggplot(data = get(paste("coins", movement.lower, "avg", sep = ".")), aes(x = Date)) +
    geom_line(aes(y = get(y_val), color = "C1"), data = get(paste("C1", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C2"), data = get(paste("C2", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C3"), data = get(paste("C3", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C4"), data = get(paste("C4", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C5"), data = get(paste("C5", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C6"), data = get(paste("C6", movement.lower, "avg", sep = "."))) +
    ylab(paste("Average", movement)) +
    labs(color = "Cluster") +
    ggtitle(paste(movement, "Movement between Clusters"))
  cluster.boxplot <- ggplot() +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C1"), data = get(paste("C1", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C2"), data = get(paste("C2", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C3"), data = get(paste("C3", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C4"), data = get(paste("C4", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C5"), data = get(paste("C5", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C6"), data = get(paste("C6", movement.lower, "avg", sep = ".")), notch = T) +
    ylab(movement) +
    labs(fill = "Cluster") +
    ggtitle(paste(movement, "Boxplot between Clusters"))
    
  print(cluster.plot)
  print(cluster.boxplot)
}

NA
NA

Peri-COVID19


# visualize coins' prices within cluster
price_cols <- c("Date", "Close", "Volume", "change", "returns", "volatility")
cluster.groups <- c("C1", "C2", "C3", "C4", "C5", "C6")

for (cluster.group in cluster.groups) {
  ndays <- 362
  coins.prices <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.changes <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.returns <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.volatility <- data.frame(matrix(nrow = ndays, ncol = 0))
  for (symbol in get(cluster.group)$symbol) {
    values <- fread(paste("datasets/daily/coins", paste(symbol, 'csv', sep = "."), sep = "/")) %>% as.data.frame() %>% filter(Date >= "2020-01-01" & Date <= "2020-12-31") %>% select(all_of(price_cols)) %>% arrange(Date)
    symbol <- str_split(symbol, "-", simplify = T)[1]
    
    coins.prices['Date'] <- values$Date
    coins.changes['Date'] <- values$Date
    coins.returns['Date'] <- values$Date
    coins.volatility['Date'] <- values$Date
    
    coins.prices[symbol] <- values$Close 
    coins.changes[symbol] <- values$change
    coins.returns[symbol] <- values$returns
    coins.volatility[symbol] <- values$volatility
  }
  
  coins.prices.melt <- reshape2::melt(coins.prices, "Date", value.name = "Prices", variable.name = "Coins")
  coins.prices.plot <- ggplot(data = coins.prices.melt, aes(x = Date, y = Prices, color = Coins)) +
    ggtitle(paste("Price Movement of", cluster.group)) +
    geom_line()
  coins.prices.boxplot <- ggplot(data = coins.prices.melt, aes(x = Coins, y = Prices, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Price Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.prices.plot)
  print(coins.prices.boxplot)
    
  coins.changes.melt <- reshape2::melt(coins.changes, "Date", value.name = "Changes", variable.name = "Coins")
  coins.changes.plot <- ggplot(data = coins.changes.melt, aes(x = Date, y = Changes, color = Coins)) +
    ggtitle(paste("Change Movement of", cluster.group)) +
    geom_line()
  coins.changes.boxplot <- ggplot(data = coins.changes.melt, aes(x = Coins, y = Changes, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Change Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.changes.plot)
  print(coins.changes.boxplot)

  coins.returns.melt <- reshape2::melt(coins.returns, "Date", value.name = "Returns", variable.name = "Coins")
  coins.returns.plot <- ggplot(data = coins.returns.melt, aes(x = Date, y = Returns, color = Coins)) +
    ggtitle(paste("Returns Movement of", cluster.group)) +
    geom_line()
  coins.returns.boxplot <- ggplot(data = coins.returns.melt, aes(x = Coins, y = Returns, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Returns Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.returns.plot)
  print(coins.returns.boxplot)

  coins.volatility.melt <- reshape2::melt(coins.volatility, "Date", value.name = "Volatility", variable.name = "Coins")
  coins.volatility.plot <- ggplot(data = coins.volatility.melt, aes(x = Date, y = Volatility, color = Coins)) +
    ggtitle(paste("Volatility Movement of", cluster.group)) +
    geom_line()
  coins.volatility.boxplot <- ggplot(data = coins.volatility.melt, aes(x = Coins, y = Volatility, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Volatility Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.volatility.plot) 
  print(coins.volatility.boxplot) 

  # average prices for inter-cluster comparison
  coins.prices.avg <- coins.prices.melt %>% group_by(Date) %>% summarise(avg_prices = mean(Prices, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.changes.avg <- coins.changes.melt %>% group_by(Date) %>% summarise(avg_changes = mean(Changes, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.returns.avg <- coins.returns.melt %>% group_by(Date) %>% summarise(avg_returns = mean(Returns, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.volatility.avg <- coins.volatility.melt %>% group_by(Date) %>% summarise(avg_volatility = mean(Volatility, na.rm = T)) %>% mutate(cluster = cluster.group)
  
  assign(paste(cluster.group, "prices.avg", sep = "."), coins.prices.avg)
  assign(paste(cluster.group, "changes.avg", sep = "."), coins.changes.avg)
  assign(paste(cluster.group, "returns.avg", sep = "."), coins.returns.avg)
  assign(paste(cluster.group, "volatility.avg", sep = "."), coins.volatility.avg)
}

# inter-cluster prices
for (movement in c("Prices", "Changes", "Returns", "Volatility")) {
  movement.lower <- tolower(movement)
  y_val <- paste("avg", movement.lower, sep = "_")
  cluster.plot <- ggplot(data = get(paste("coins", movement.lower, "avg", sep = ".")), aes(x = Date)) +
    geom_line(aes(y = get(y_val), color = "C1"), data = get(paste("C1", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C2"), data = get(paste("C2", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C3"), data = get(paste("C3", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C4"), data = get(paste("C4", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C5"), data = get(paste("C5", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C6"), data = get(paste("C6", movement.lower, "avg", sep = "."))) +
    ylab(paste("Average", movement)) +
    labs(color = "Cluster") +
    ggtitle(paste(movement, "Movement between Clusters"))
  cluster.boxplot <- ggplot() +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C1"), data = get(paste("C1", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C2"), data = get(paste("C2", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C3"), data = get(paste("C3", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C4"), data = get(paste("C4", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C5"), data = get(paste("C5", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C6"), data = get(paste("C6", movement.lower, "avg", sep = ".")), notch = T) +
    ylab(movement) +
    labs(fill = "Cluster") +
    ggtitle(paste(movement, "Boxplot between Clusters"))
    
  print(cluster.plot)
  print(cluster.boxplot)
}

Post-COVID19



# visualize coins' prices within cluster
price_cols <- c("Date", "Close", "Volume", "change", "returns", "volatility")
cluster.groups <- c("C1", "C2", "C3", "C4", "C5", "C6")

for (cluster.group in cluster.groups) {
  ndays <- 304
  coins.prices <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.changes <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.returns <- data.frame(matrix(nrow = ndays, ncol = 0))
  coins.volatility <- data.frame(matrix(nrow = ndays, ncol = 0))
  for (symbol in get(cluster.group)$symbol) {
    values <- fread(paste("datasets/daily/coins", paste(symbol, 'csv', sep = "."), sep = "/")) %>% as.data.frame() %>% filter(Date >= "2021-01-01") %>% select(all_of(price_cols)) %>% arrange(Date)
    symbol <- str_split(symbol, "-", simplify = T)[1]
    
    coins.prices['Date'] <- values$Date
    coins.changes['Date'] <- values$Date
    coins.returns['Date'] <- values$Date
    coins.volatility['Date'] <- values$Date
    
    coins.prices[symbol] <- values$Close 
    coins.changes[symbol] <- values$change
    coins.returns[symbol] <- values$returns
    coins.volatility[symbol] <- values$volatility
  }
  
  coins.prices.melt <- reshape2::melt(coins.prices, "Date", value.name = "Prices", variable.name = "Coins")
  coins.prices.plot <- ggplot(data = coins.prices.melt, aes(x = Date, y = Prices, color = Coins)) +
    ggtitle(paste("Price Movement of", cluster.group)) +
    geom_line()
  coins.prices.boxplot <- ggplot(data = coins.prices.melt, aes(x = Coins, y = Prices, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Price Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.prices.plot)
  print(coins.prices.boxplot)
    
  coins.changes.melt <- reshape2::melt(coins.changes, "Date", value.name = "Changes", variable.name = "Coins")
  coins.changes.plot <- ggplot(data = coins.changes.melt, aes(x = Date, y = Changes, color = Coins)) +
    ggtitle(paste("Change Movement of", cluster.group)) +
    geom_line()
  coins.changes.boxplot <- ggplot(data = coins.changes.melt, aes(x = Coins, y = Changes, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Change Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.changes.plot)
  print(coins.changes.boxplot)

  coins.returns.melt <- reshape2::melt(coins.returns, "Date", value.name = "Returns", variable.name = "Coins")
  coins.returns.plot <- ggplot(data = coins.returns.melt, aes(x = Date, y = Returns, color = Coins)) +
    ggtitle(paste("Returns Movement of", cluster.group)) +
    geom_line()
  coins.returns.boxplot <- ggplot(data = coins.returns.melt, aes(x = Coins, y = Returns, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Returns Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.returns.plot)
  print(coins.returns.boxplot)

  coins.volatility.melt <- reshape2::melt(coins.volatility, "Date", value.name = "Volatility", variable.name = "Coins")
  coins.volatility.plot <- ggplot(data = coins.volatility.melt, aes(x = Date, y = Volatility, color = Coins)) +
    ggtitle(paste("Volatility Movement of", cluster.group)) +
    geom_line()
  coins.volatility.boxplot <- ggplot(data = coins.volatility.melt, aes(x = Coins, y = Volatility, fill = Coins)) +
    geom_boxplot(notch = T) +
    ggtitle(paste("Volatility Boxplot of", cluster.group)) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
  print(coins.volatility.plot) 
  print(coins.volatility.boxplot) 

  # average prices for inter-cluster comparison
  coins.prices.avg <- coins.prices.melt %>% group_by(Date) %>% summarise(avg_prices = mean(Prices, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.changes.avg <- coins.changes.melt %>% group_by(Date) %>% summarise(avg_changes = mean(Changes, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.returns.avg <- coins.returns.melt %>% group_by(Date) %>% summarise(avg_returns = mean(Returns, na.rm = T)) %>% mutate(cluster = cluster.group)
  coins.volatility.avg <- coins.volatility.melt %>% group_by(Date) %>% summarise(avg_volatility = mean(Volatility, na.rm = T)) %>% mutate(cluster = cluster.group)
  
  assign(paste(cluster.group, "prices.avg", sep = "."), coins.prices.avg)
  assign(paste(cluster.group, "changes.avg", sep = "."), coins.changes.avg)
  assign(paste(cluster.group, "returns.avg", sep = "."), coins.returns.avg)
  assign(paste(cluster.group, "volatility.avg", sep = "."), coins.volatility.avg)
}

# inter-cluster prices
for (movement in c("Prices", "Changes", "Returns", "Volatility")) {
  movement.lower <- tolower(movement)
  y_val <- paste("avg", movement.lower, sep = "_")
  cluster.plot <- ggplot(data = get(paste("coins", movement.lower, "avg", sep = ".")), aes(x = Date)) +
    geom_line(aes(y = get(y_val), color = "C1"), data = get(paste("C1", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C2"), data = get(paste("C2", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C3"), data = get(paste("C3", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C4"), data = get(paste("C4", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C5"), data = get(paste("C5", movement.lower, "avg", sep = "."))) +
    geom_line(aes(y = get(y_val), color = "C6"), data = get(paste("C6", movement.lower, "avg", sep = "."))) +
    ylab(paste("Average", movement)) +
    labs(color = "Cluster") +
    ggtitle(paste(movement, "Movement between Clusters"))
  cluster.boxplot <- ggplot() +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C1"), data = get(paste("C1", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C2"), data = get(paste("C2", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C3"), data = get(paste("C3", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C4"), data = get(paste("C4", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C5"), data = get(paste("C5", movement.lower, "avg", sep = ".")), notch = T) +
    geom_boxplot(aes(y = get(y_val), x = cluster, fill = "C6"), data = get(paste("C6", movement.lower, "avg", sep = ".")), notch = T) +
    ylab(movement) +
    labs(fill = "Cluster") +
    ggtitle(paste(movement, "Boxplot between Clusters"))
    
  print(cluster.plot)
  print(cluster.boxplot)
}
LS0tCnRpdGxlOiAiUlEzIgphdXRob3I6ICJDUzU2NCBUZWFtIDEwIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpMb2FkIERhdGFzZXRzCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZGF0YS50YWJsZSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShyZWFkeGwpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RyaW5ncikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGdjbHVzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KE5iQ2x1c3QpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZmFzdER1bW1pZXMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoY2x1c3RlcikpCgpzZXQuc2VlZCg3KQp0aGVtZV9zZXQodGhlbWVfYncoKSkKdml6X3BhdGggPC0gInZpc3VhbGl6YXRpb25zL1JRMyIKCiMgTWV0YWRhdGEKY29pbl9mZWF0dXJlcyA8LSBmcmVhZCgiZGF0YXNldHMvY29pbl9mZWF0dXJlcy5jc3YiKSAlPiUgYXMuZGF0YS5mcmFtZSgpCnRpY2tlcl9pbmZvIDwtIGZyZWFkKCJkYXRhc2V0cy90aWNrZXJfaW5mby5jc3YiKSAlPiUgYXMuZGF0YS5mcmFtZSgpCgpjb2luX2ZlYXR1cmVzIDwtIGNvaW5fZmVhdHVyZXMgJT4lIHNlbGVjdCgtZWNvc3lzdGVtKSAlPiUgbXV0YXRlKG1pbmVhYmlsaXR5ID0gaWZfZWxzZShtaW5lYWJpbGl0eSwgMSwgMCkpCmNvaW5fZmVhdHVyZXMgPC0gZHVtbXlfY29scyhjb2luX2ZlYXR1cmVzLCBzZWxlY3RfY29sdW1ucyA9IGMoImNvbnNlbnN1cyIsICJoYXNoIiksIHJlbW92ZV9zZWxlY3RlZF9jb2x1bW5zID0gVCkKYGBgCgoKYGBge3J9CgojIEVycm9yIEluZGV4OiBjY2MsIHNjb3R0LCBtYXJyaW90LCB0cmNvdncsIHRyYWNldywgZnJpZWRtYW4sIHJ1YmluCmluZGljZXMgPC1jKCJrbCIsICJjaCIsICJoYXJ0aWdhbiIsICJjaW5kZXgiLCAiZGIiLCAic2lsaG91ZXR0ZSIsICJkdWRhIiwgInBzZXVkb3QyIiwgImJlYWxlIiwgInJhdGtvd3NreSIsICJiYWxsIiwgInB0YmlzZXJpYWwiLCAiZ2FwIiwgImZyZXkiLCAibWNjbGFpbiIsICJnYW1tYSIsICJncGx1cyIsICJ0YXUiLCAiZHVubiIsICJodWJlcnQiLCAic2RpbmRleCIsICJkaW5kZXgiLCAic2RidyIpCgpjbHMuZmVhdHVyZXMgPC0gc2VsZWN0KGNvaW5fZmVhdHVyZXMsIC0xKQoKbmNfbGlzdCA8LSBjKCkKZm9yIChpZHggaW4gaW5kaWNlcyl7CiAgbmMgPC0gTmJDbHVzdChjbHMuZmVhdHVyZXMsIG1ldGhvZD0iY29tcGxldGUiLCBpbmRleD1pZHgpJEJlc3QubmMgIyBmaW5kIG51bWJlciBvZiBjbHVzdGVycwogIG5jX2xpc3QgPC0gYyhuY19saXN0LCBuY1sxXSkKfQoKc3RhdF9tb2RlIDwtIGZ1bmN0aW9uKHYpIHsKIHVuaXF2IDwtIHVuaXF1ZSh2KQogdW5pcXZbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHYsIHVuaXF2KSkpXQp9CgpuYyA8LSBzdGF0X21vZGUobmNfbGlzdCkKYGBgCgoKYGBge3J9CnJvd25hbWVzKGNscy5mZWF0dXJlcykgPC0gY29pbl9mZWF0dXJlcyRzeW1ib2wKY2xzLmttZWFucyA8LSBrbWVhbnMoY2xzLmZlYXR1cmVzLCBjZW50ZXJzID0gbmMpICMgcnVuIGstbWVhbiBjbHVzdGVyaW5nCmNscy5oY2x1c3QgPC0gaGNsdXN0KGRpc3QoY2xzLmZlYXR1cmVzLCBtZXRob2QgPSAiZXVjbGlkZWFuIiksIG1ldGhvZCA9ICJjb21wbGV0ZSIpICMgcnVuIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCgpgYGAKCgpgYGB7cn0KCiMgdmlzdWFsaXplIGNsdXN0ZXJzCmNsdXNwbG90KGNscy5mZWF0dXJlcywgY2xzLmttZWFucyRjbHVzdGVyLCBjb2xvciA9IFQsIHNoYWRlID0gVCwgbGFiZWxzID0gMywgbWFpbiA9ICJDb2luIENsdXN0ZXJzIikKCnBsb3QoY2xzLmhjbHVzdCwgbGFiZWxzID0gY29pbl9mZWF0dXJlcyRzeW1ib2wsIGNleCA9IDAuOCkKcmVjdC5oY2x1c3QoY2xzLmhjbHVzdCwgNikKCmNscy5oY2x1c3QuY24gPC0gY3V0cmVlKGNscy5oY2x1c3QsIGsgPSA2KQoKcGFtZCA8LSBwYW0oZGlzdChjbHMuZmVhdHVyZXMsIG1ldGhvZCA9ICJldWNsaWRlYW4iKSwgNikKCnNvYmogPC0gc2lsaG91ZXR0ZShwYW1kKQpwbG90KHNvYmosIGNvbD0yOjcpCgpgYGAKCgpgYGB7cn0KbGlicmFyeSh0aWR5cikKCiMgaW50ZXJwcmV0IGVhY2ggY2x1c3RlcgpmY2x1c3RlciA8LSBhcy5kYXRhLmZyYW1lKGNscy5oY2x1c3QuY24pIApjb2xuYW1lcyhmY2x1c3RlcikgPC0gYygiY2x1c3RlciIpCgpjb2luX2ZlYXR1cmVzLmNsdXN0ZXIgPC0gYmluZF9jb2xzKGxpc3QoY29pbl9mZWF0dXJlcywgZmNsdXN0ZXIpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBhcy5mYWN0b3IoY2x1c3RlcikpCmNscy5oY2x1c3Quc3RhdHMgPC0gYmluZF9jb2xzKGxpc3QoY2xzLmZlYXR1cmVzLCBmY2x1c3RlcikpICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgc3VtbWFyaXNlX2FsbChsaXN0KG1lYW4pKSAlPiUgZ2F0aGVyKCJ0cmFpdHMiLCAibWVhbnZhbCIsIDI6NDYpICU+JSBtdXRhdGUoY2x1c3RlciA9IGFzLmZhY3RvcihjbHVzdGVyKSkKCmNscy5oY2x1c3Quc3RhdHMgJT4lIGdncGxvdChhZXMoeCA9IHRyYWl0cywgeSA9IG1lYW52YWwsIGZpbGwgPSBjbHVzdGVyKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2d0aXRsZSgiQXZlcmFnZSBGZWF0dXJlcyBieSBDbHVzdGVycyIpICsKICB4bGFiKCIiKSArIAogIHlsYWIoIkF2ZXJhZ2UgVmFsdWVzIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCgpgYGB7cn0KIyBmaWx0ZXIgYnkgZWFjaCBjbHVzdGVyCkMxIDwtIGNvaW5fZmVhdHVyZXMuY2x1c3RlciAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gIjEiKQpDMiA8LSBjb2luX2ZlYXR1cmVzLmNsdXN0ZXIgJT4lIGZpbHRlcihjbHVzdGVyID09ICIyIikKQzMgPC0gY29pbl9mZWF0dXJlcy5jbHVzdGVyICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAiMyIpCkM0IDwtIGNvaW5fZmVhdHVyZXMuY2x1c3RlciAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gIjQiKQpDNSA8LSBjb2luX2ZlYXR1cmVzLmNsdXN0ZXIgJT4lIGZpbHRlcihjbHVzdGVyID09ICI1IikKQzYgPC0gY29pbl9mZWF0dXJlcy5jbHVzdGVyICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAiNiIpCmBgYAoKClByZS1DT1ZJRDE5CmBgYHtyfQojIHZpc3VhbGl6ZSBjb2lucycgcHJpY2VzIHdpdGhpbiBjbHVzdGVyCnByaWNlX2NvbHMgPC0gYygiRGF0ZSIsICJDbG9zZSIsICJWb2x1bWUiLCAiY2hhbmdlIiwgInJldHVybnMiLCAidm9sYXRpbGl0eSIpCmNsdXN0ZXIuZ3JvdXBzIDwtIGMoIkMxIiwgIkMyIiwgIkMzIiwgIkM0IiwgIkM1IiwgIkM2IikKCmZvciAoY2x1c3Rlci5ncm91cCBpbiBjbHVzdGVyLmdyb3VwcykgewogIG5kYXlzIDwtIDM2NQogIGNvaW5zLnByaWNlcyA8LSBkYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbmRheXMsIG5jb2wgPSAwKSkKICBjb2lucy5jaGFuZ2VzIDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBuZGF5cywgbmNvbCA9IDApKQogIGNvaW5zLnJldHVybnMgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5kYXlzLCBuY29sID0gMCkpCiAgY29pbnMudm9sYXRpbGl0eSA8LSBkYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbmRheXMsIG5jb2wgPSAwKSkKICBmb3IgKHN5bWJvbCBpbiBnZXQoY2x1c3Rlci5ncm91cCkkc3ltYm9sKSB7CiAgICB2YWx1ZXMgPC0gZnJlYWQocGFzdGUoImRhdGFzZXRzL2RhaWx5L2NvaW5zIiwgcGFzdGUoc3ltYm9sLCAnY3N2Jywgc2VwID0gIi4iKSwgc2VwID0gIi8iKSkgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKERhdGUgPj0gIjIwMTktMDEtMDEiICYgRGF0ZSA8PSAiMjAxOS0xMi0zMSIpICU+JSBzZWxlY3QoYWxsX29mKHByaWNlX2NvbHMpKSAlPiUgYXJyYW5nZShEYXRlKQogICAgc3ltYm9sIDwtIHN0cl9zcGxpdChzeW1ib2wsICItIiwgc2ltcGxpZnkgPSBUKVsxXQogICAgCiAgICBjb2lucy5wcmljZXNbJ0RhdGUnXSA8LSB2YWx1ZXMkRGF0ZQogICAgY29pbnMuY2hhbmdlc1snRGF0ZSddIDwtIHZhbHVlcyREYXRlCiAgICBjb2lucy5yZXR1cm5zWydEYXRlJ10gPC0gdmFsdWVzJERhdGUKICAgIGNvaW5zLnZvbGF0aWxpdHlbJ0RhdGUnXSA8LSB2YWx1ZXMkRGF0ZQogICAgCiAgICBjb2lucy5wcmljZXNbc3ltYm9sXSA8LSB2YWx1ZXMkQ2xvc2UgCiAgICBjb2lucy5jaGFuZ2VzW3N5bWJvbF0gPC0gdmFsdWVzJGNoYW5nZQogICAgY29pbnMucmV0dXJuc1tzeW1ib2xdIDwtIHZhbHVlcyRyZXR1cm5zCiAgICBjb2lucy52b2xhdGlsaXR5W3N5bWJvbF0gPC0gdmFsdWVzJHZvbGF0aWxpdHkKICB9CiAgCiAgY29pbnMucHJpY2VzLm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMucHJpY2VzLCAiRGF0ZSIsIHZhbHVlLm5hbWUgPSAiUHJpY2VzIiwgdmFyaWFibGUubmFtZSA9ICJDb2lucyIpCiAgY29pbnMucHJpY2VzLnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5wcmljZXMubWVsdCwgYWVzKHggPSBEYXRlLCB5ID0gUHJpY2VzLCBjb2xvciA9IENvaW5zKSkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUHJpY2UgTW92ZW1lbnQgb2YiLCBjbHVzdGVyLmdyb3VwKSkgKwogICAgZ2VvbV9saW5lKCkKICBjb2lucy5wcmljZXMuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLnByaWNlcy5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gUHJpY2VzLCBmaWxsID0gQ29pbnMpKSArCiAgICBnZW9tX2JveHBsb3Qobm90Y2ggPSBUKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJQcmljZSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5wcmljZXMucGxvdCkKICBwcmludChjb2lucy5wcmljZXMuYm94cGxvdCkKICAgIAogIGNvaW5zLmNoYW5nZXMubWVsdCA8LSByZXNoYXBlMjo6bWVsdChjb2lucy5jaGFuZ2VzLCAiRGF0ZSIsIHZhbHVlLm5hbWUgPSAiQ2hhbmdlcyIsIHZhcmlhYmxlLm5hbWUgPSAiQ29pbnMiKQogIGNvaW5zLmNoYW5nZXMucGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLmNoYW5nZXMubWVsdCwgYWVzKHggPSBEYXRlLCB5ID0gQ2hhbmdlcywgY29sb3IgPSBDb2lucykpICsKICAgIGdndGl0bGUocGFzdGUoIkNoYW5nZSBNb3ZlbWVudCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICBnZW9tX2xpbmUoKQogIGNvaW5zLmNoYW5nZXMuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLmNoYW5nZXMubWVsdCwgYWVzKHggPSBDb2lucywgeSA9IENoYW5nZXMsIGZpbGwgPSBDb2lucykpICsKICAgIGdlb21fYm94cGxvdChub3RjaCA9IFQpICsKICAgIGdndGl0bGUocGFzdGUoIkNoYW5nZSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5jaGFuZ2VzLnBsb3QpCiAgcHJpbnQoY29pbnMuY2hhbmdlcy5ib3hwbG90KQoKICBjb2lucy5yZXR1cm5zLm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMucmV0dXJucywgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIlJldHVybnMiLCB2YXJpYWJsZS5uYW1lID0gIkNvaW5zIikKICBjb2lucy5yZXR1cm5zLnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5yZXR1cm5zLm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IFJldHVybnMsIGNvbG9yID0gQ29pbnMpKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJSZXR1cm5zIE1vdmVtZW50IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIGdlb21fbGluZSgpCiAgY29pbnMucmV0dXJucy5ib3hwbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMucmV0dXJucy5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gUmV0dXJucywgZmlsbCA9IENvaW5zKSkgKwogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUmV0dXJucyBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5yZXR1cm5zLnBsb3QpCiAgcHJpbnQoY29pbnMucmV0dXJucy5ib3hwbG90KQoKICBjb2lucy52b2xhdGlsaXR5Lm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMudm9sYXRpbGl0eSwgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIlZvbGF0aWxpdHkiLCB2YXJpYWJsZS5uYW1lID0gIkNvaW5zIikKICBjb2lucy52b2xhdGlsaXR5LnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy52b2xhdGlsaXR5Lm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IFZvbGF0aWxpdHksIGNvbG9yID0gQ29pbnMpKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJWb2xhdGlsaXR5IE1vdmVtZW50IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIGdlb21fbGluZSgpCiAgY29pbnMudm9sYXRpbGl0eS5ib3hwbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMudm9sYXRpbGl0eS5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gVm9sYXRpbGl0eSwgZmlsbCA9IENvaW5zKSkgKwogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkgKwogICAgZ2d0aXRsZShwYXN0ZSgiVm9sYXRpbGl0eSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy52b2xhdGlsaXR5LnBsb3QpIAogIHByaW50KGNvaW5zLnZvbGF0aWxpdHkuYm94cGxvdCkgCgogICMgYXZlcmFnZSBwcmljZXMgZm9yIGludGVyLWNsdXN0ZXIgY29tcGFyaXNvbgogIGNvaW5zLnByaWNlcy5hdmcgPC0gY29pbnMucHJpY2VzLm1lbHQgJT4lIGdyb3VwX2J5KERhdGUpICU+JSBzdW1tYXJpc2UoYXZnX3ByaWNlcyA9IG1lYW4oUHJpY2VzLCBuYS5ybSA9IFQpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyLmdyb3VwKQogIGNvaW5zLmNoYW5nZXMuYXZnIDwtIGNvaW5zLmNoYW5nZXMubWVsdCAlPiUgZ3JvdXBfYnkoRGF0ZSkgJT4lIHN1bW1hcmlzZShhdmdfY2hhbmdlcyA9IG1lYW4oQ2hhbmdlcywgbmEucm0gPSBUKSkgJT4lIG11dGF0ZShjbHVzdGVyID0gY2x1c3Rlci5ncm91cCkKICBjb2lucy5yZXR1cm5zLmF2ZyA8LSBjb2lucy5yZXR1cm5zLm1lbHQgJT4lIGdyb3VwX2J5KERhdGUpICU+JSBzdW1tYXJpc2UoYXZnX3JldHVybnMgPSBtZWFuKFJldHVybnMsIG5hLnJtID0gVCkpICU+JSBtdXRhdGUoY2x1c3RlciA9IGNsdXN0ZXIuZ3JvdXApCiAgY29pbnMudm9sYXRpbGl0eS5hdmcgPC0gY29pbnMudm9sYXRpbGl0eS5tZWx0ICU+JSBncm91cF9ieShEYXRlKSAlPiUgc3VtbWFyaXNlKGF2Z192b2xhdGlsaXR5ID0gbWVhbihWb2xhdGlsaXR5LCBuYS5ybSA9IFQpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyLmdyb3VwKQogIAogIGFzc2lnbihwYXN0ZShjbHVzdGVyLmdyb3VwLCAicHJpY2VzLmF2ZyIsIHNlcCA9ICIuIiksIGNvaW5zLnByaWNlcy5hdmcpCiAgYXNzaWduKHBhc3RlKGNsdXN0ZXIuZ3JvdXAsICJjaGFuZ2VzLmF2ZyIsIHNlcCA9ICIuIiksIGNvaW5zLmNoYW5nZXMuYXZnKQogIGFzc2lnbihwYXN0ZShjbHVzdGVyLmdyb3VwLCAicmV0dXJucy5hdmciLCBzZXAgPSAiLiIpLCBjb2lucy5yZXR1cm5zLmF2ZykKICBhc3NpZ24ocGFzdGUoY2x1c3Rlci5ncm91cCwgInZvbGF0aWxpdHkuYXZnIiwgc2VwID0gIi4iKSwgY29pbnMudm9sYXRpbGl0eS5hdmcpCn0KCiMgaW50ZXItY2x1c3RlciBwcmljZXMKZm9yIChtb3ZlbWVudCBpbiBjKCJQcmljZXMiLCAiQ2hhbmdlcyIsICJSZXR1cm5zIiwgIlZvbGF0aWxpdHkiKSkgewogIG1vdmVtZW50Lmxvd2VyIDwtIHRvbG93ZXIobW92ZW1lbnQpCiAgeV92YWwgPC0gcGFzdGUoImF2ZyIsIG1vdmVtZW50Lmxvd2VyLCBzZXAgPSAiXyIpCiAgY2x1c3Rlci5wbG90IDwtIGdncGxvdChkYXRhID0gZ2V0KHBhc3RlKCJjb2lucyIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIGFlcyh4ID0gRGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkMxIiksIGRhdGEgPSBnZXQocGFzdGUoIkMxIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzIiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzIiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDMyIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMyIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkM0IiksIGRhdGEgPSBnZXQocGFzdGUoIkM0IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzUiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzUiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDNiIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNiIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIHlsYWIocGFzdGUoIkF2ZXJhZ2UiLCBtb3ZlbWVudCkpICsKICAgIGxhYnMoY29sb3IgPSAiQ2x1c3RlciIpICsKICAgIGdndGl0bGUocGFzdGUobW92ZW1lbnQsICJNb3ZlbWVudCBiZXR3ZWVuIENsdXN0ZXJzIikpCiAgY2x1c3Rlci5ib3hwbG90IDwtIGdncGxvdCgpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkMxIiksIGRhdGEgPSBnZXQocGFzdGUoIkMxIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDMiIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMiIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzMiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzMiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkM0IiksIGRhdGEgPSBnZXQocGFzdGUoIkM0IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDNSIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNSIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzYiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzYiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIHlsYWIobW92ZW1lbnQpICsKICAgIGxhYnMoZmlsbCA9ICJDbHVzdGVyIikgKwogICAgZ2d0aXRsZShwYXN0ZShtb3ZlbWVudCwgIkJveHBsb3QgYmV0d2VlbiBDbHVzdGVycyIpKQogICAgCiAgcHJpbnQoY2x1c3Rlci5wbG90KQogIHByaW50KGNsdXN0ZXIuYm94cGxvdCkKfQoKCmBgYAoKUGVyaS1DT1ZJRDE5CmBgYHtyfQoKIyB2aXN1YWxpemUgY29pbnMnIHByaWNlcyB3aXRoaW4gY2x1c3RlcgpwcmljZV9jb2xzIDwtIGMoIkRhdGUiLCAiQ2xvc2UiLCAiVm9sdW1lIiwgImNoYW5nZSIsICJyZXR1cm5zIiwgInZvbGF0aWxpdHkiKQpjbHVzdGVyLmdyb3VwcyA8LSBjKCJDMSIsICJDMiIsICJDMyIsICJDNCIsICJDNSIsICJDNiIpCgpmb3IgKGNsdXN0ZXIuZ3JvdXAgaW4gY2x1c3Rlci5ncm91cHMpIHsKICBuZGF5cyA8LSAzNjIKICBjb2lucy5wcmljZXMgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5kYXlzLCBuY29sID0gMCkpCiAgY29pbnMuY2hhbmdlcyA8LSBkYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbmRheXMsIG5jb2wgPSAwKSkKICBjb2lucy5yZXR1cm5zIDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBuZGF5cywgbmNvbCA9IDApKQogIGNvaW5zLnZvbGF0aWxpdHkgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5kYXlzLCBuY29sID0gMCkpCiAgZm9yIChzeW1ib2wgaW4gZ2V0KGNsdXN0ZXIuZ3JvdXApJHN5bWJvbCkgewogICAgdmFsdWVzIDwtIGZyZWFkKHBhc3RlKCJkYXRhc2V0cy9kYWlseS9jb2lucyIsIHBhc3RlKHN5bWJvbCwgJ2NzdicsIHNlcCA9ICIuIiksIHNlcCA9ICIvIikpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihEYXRlID49ICIyMDIwLTAxLTAxIiAmIERhdGUgPD0gIjIwMjAtMTItMzEiKSAlPiUgc2VsZWN0KGFsbF9vZihwcmljZV9jb2xzKSkgJT4lIGFycmFuZ2UoRGF0ZSkKICAgIHN5bWJvbCA8LSBzdHJfc3BsaXQoc3ltYm9sLCAiLSIsIHNpbXBsaWZ5ID0gVClbMV0KICAgIAogICAgY29pbnMucHJpY2VzWydEYXRlJ10gPC0gdmFsdWVzJERhdGUKICAgIGNvaW5zLmNoYW5nZXNbJ0RhdGUnXSA8LSB2YWx1ZXMkRGF0ZQogICAgY29pbnMucmV0dXJuc1snRGF0ZSddIDwtIHZhbHVlcyREYXRlCiAgICBjb2lucy52b2xhdGlsaXR5WydEYXRlJ10gPC0gdmFsdWVzJERhdGUKICAgIAogICAgY29pbnMucHJpY2VzW3N5bWJvbF0gPC0gdmFsdWVzJENsb3NlIAogICAgY29pbnMuY2hhbmdlc1tzeW1ib2xdIDwtIHZhbHVlcyRjaGFuZ2UKICAgIGNvaW5zLnJldHVybnNbc3ltYm9sXSA8LSB2YWx1ZXMkcmV0dXJucwogICAgY29pbnMudm9sYXRpbGl0eVtzeW1ib2xdIDwtIHZhbHVlcyR2b2xhdGlsaXR5CiAgfQogIAogIGNvaW5zLnByaWNlcy5tZWx0IDwtIHJlc2hhcGUyOjptZWx0KGNvaW5zLnByaWNlcywgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIlByaWNlcyIsIHZhcmlhYmxlLm5hbWUgPSAiQ29pbnMiKQogIGNvaW5zLnByaWNlcy5wbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMucHJpY2VzLm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IFByaWNlcywgY29sb3IgPSBDb2lucykpICsKICAgIGdndGl0bGUocGFzdGUoIlByaWNlIE1vdmVtZW50IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIGdlb21fbGluZSgpCiAgY29pbnMucHJpY2VzLmJveHBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5wcmljZXMubWVsdCwgYWVzKHggPSBDb2lucywgeSA9IFByaWNlcywgZmlsbCA9IENvaW5zKSkgKwogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUHJpY2UgQm94cGxvdCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCiAgcHJpbnQoY29pbnMucHJpY2VzLnBsb3QpCiAgcHJpbnQoY29pbnMucHJpY2VzLmJveHBsb3QpCiAgICAKICBjb2lucy5jaGFuZ2VzLm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMuY2hhbmdlcywgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIkNoYW5nZXMiLCB2YXJpYWJsZS5uYW1lID0gIkNvaW5zIikKICBjb2lucy5jaGFuZ2VzLnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5jaGFuZ2VzLm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IENoYW5nZXMsIGNvbG9yID0gQ29pbnMpKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJDaGFuZ2UgTW92ZW1lbnQgb2YiLCBjbHVzdGVyLmdyb3VwKSkgKwogICAgZ2VvbV9saW5lKCkKICBjb2lucy5jaGFuZ2VzLmJveHBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5jaGFuZ2VzLm1lbHQsIGFlcyh4ID0gQ29pbnMsIHkgPSBDaGFuZ2VzLCBmaWxsID0gQ29pbnMpKSArCiAgICBnZW9tX2JveHBsb3Qobm90Y2ggPSBUKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJDaGFuZ2UgQm94cGxvdCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCiAgcHJpbnQoY29pbnMuY2hhbmdlcy5wbG90KQogIHByaW50KGNvaW5zLmNoYW5nZXMuYm94cGxvdCkKCiAgY29pbnMucmV0dXJucy5tZWx0IDwtIHJlc2hhcGUyOjptZWx0KGNvaW5zLnJldHVybnMsICJEYXRlIiwgdmFsdWUubmFtZSA9ICJSZXR1cm5zIiwgdmFyaWFibGUubmFtZSA9ICJDb2lucyIpCiAgY29pbnMucmV0dXJucy5wbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMucmV0dXJucy5tZWx0LCBhZXMoeCA9IERhdGUsIHkgPSBSZXR1cm5zLCBjb2xvciA9IENvaW5zKSkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUmV0dXJucyBNb3ZlbWVudCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICBnZW9tX2xpbmUoKQogIGNvaW5zLnJldHVybnMuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLnJldHVybnMubWVsdCwgYWVzKHggPSBDb2lucywgeSA9IFJldHVybnMsIGZpbGwgPSBDb2lucykpICsKICAgIGdlb21fYm94cGxvdChub3RjaCA9IFQpICsKICAgIGdndGl0bGUocGFzdGUoIlJldHVybnMgQm94cGxvdCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCiAgcHJpbnQoY29pbnMucmV0dXJucy5wbG90KQogIHByaW50KGNvaW5zLnJldHVybnMuYm94cGxvdCkKCiAgY29pbnMudm9sYXRpbGl0eS5tZWx0IDwtIHJlc2hhcGUyOjptZWx0KGNvaW5zLnZvbGF0aWxpdHksICJEYXRlIiwgdmFsdWUubmFtZSA9ICJWb2xhdGlsaXR5IiwgdmFyaWFibGUubmFtZSA9ICJDb2lucyIpCiAgY29pbnMudm9sYXRpbGl0eS5wbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMudm9sYXRpbGl0eS5tZWx0LCBhZXMoeCA9IERhdGUsIHkgPSBWb2xhdGlsaXR5LCBjb2xvciA9IENvaW5zKSkgKwogICAgZ2d0aXRsZShwYXN0ZSgiVm9sYXRpbGl0eSBNb3ZlbWVudCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICBnZW9tX2xpbmUoKQogIGNvaW5zLnZvbGF0aWxpdHkuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLnZvbGF0aWxpdHkubWVsdCwgYWVzKHggPSBDb2lucywgeSA9IFZvbGF0aWxpdHksIGZpbGwgPSBDb2lucykpICsKICAgIGdlb21fYm94cGxvdChub3RjaCA9IFQpICsKICAgIGdndGl0bGUocGFzdGUoIlZvbGF0aWxpdHkgQm94cGxvdCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCiAgcHJpbnQoY29pbnMudm9sYXRpbGl0eS5wbG90KSAKICBwcmludChjb2lucy52b2xhdGlsaXR5LmJveHBsb3QpIAoKICAjIGF2ZXJhZ2UgcHJpY2VzIGZvciBpbnRlci1jbHVzdGVyIGNvbXBhcmlzb24KICBjb2lucy5wcmljZXMuYXZnIDwtIGNvaW5zLnByaWNlcy5tZWx0ICU+JSBncm91cF9ieShEYXRlKSAlPiUgc3VtbWFyaXNlKGF2Z19wcmljZXMgPSBtZWFuKFByaWNlcywgbmEucm0gPSBUKSkgJT4lIG11dGF0ZShjbHVzdGVyID0gY2x1c3Rlci5ncm91cCkKICBjb2lucy5jaGFuZ2VzLmF2ZyA8LSBjb2lucy5jaGFuZ2VzLm1lbHQgJT4lIGdyb3VwX2J5KERhdGUpICU+JSBzdW1tYXJpc2UoYXZnX2NoYW5nZXMgPSBtZWFuKENoYW5nZXMsIG5hLnJtID0gVCkpICU+JSBtdXRhdGUoY2x1c3RlciA9IGNsdXN0ZXIuZ3JvdXApCiAgY29pbnMucmV0dXJucy5hdmcgPC0gY29pbnMucmV0dXJucy5tZWx0ICU+JSBncm91cF9ieShEYXRlKSAlPiUgc3VtbWFyaXNlKGF2Z19yZXR1cm5zID0gbWVhbihSZXR1cm5zLCBuYS5ybSA9IFQpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyLmdyb3VwKQogIGNvaW5zLnZvbGF0aWxpdHkuYXZnIDwtIGNvaW5zLnZvbGF0aWxpdHkubWVsdCAlPiUgZ3JvdXBfYnkoRGF0ZSkgJT4lIHN1bW1hcmlzZShhdmdfdm9sYXRpbGl0eSA9IG1lYW4oVm9sYXRpbGl0eSwgbmEucm0gPSBUKSkgJT4lIG11dGF0ZShjbHVzdGVyID0gY2x1c3Rlci5ncm91cCkKICAKICBhc3NpZ24ocGFzdGUoY2x1c3Rlci5ncm91cCwgInByaWNlcy5hdmciLCBzZXAgPSAiLiIpLCBjb2lucy5wcmljZXMuYXZnKQogIGFzc2lnbihwYXN0ZShjbHVzdGVyLmdyb3VwLCAiY2hhbmdlcy5hdmciLCBzZXAgPSAiLiIpLCBjb2lucy5jaGFuZ2VzLmF2ZykKICBhc3NpZ24ocGFzdGUoY2x1c3Rlci5ncm91cCwgInJldHVybnMuYXZnIiwgc2VwID0gIi4iKSwgY29pbnMucmV0dXJucy5hdmcpCiAgYXNzaWduKHBhc3RlKGNsdXN0ZXIuZ3JvdXAsICJ2b2xhdGlsaXR5LmF2ZyIsIHNlcCA9ICIuIiksIGNvaW5zLnZvbGF0aWxpdHkuYXZnKQp9CgojIGludGVyLWNsdXN0ZXIgcHJpY2VzCmZvciAobW92ZW1lbnQgaW4gYygiUHJpY2VzIiwgIkNoYW5nZXMiLCAiUmV0dXJucyIsICJWb2xhdGlsaXR5IikpIHsKICBtb3ZlbWVudC5sb3dlciA8LSB0b2xvd2VyKG1vdmVtZW50KQogIHlfdmFsIDwtIHBhc3RlKCJhdmciLCBtb3ZlbWVudC5sb3dlciwgc2VwID0gIl8iKQogIGNsdXN0ZXIucGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGdldChwYXN0ZSgiY29pbnMiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBhZXMoeCA9IERhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDMSIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMSIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkMyIiksIGRhdGEgPSBnZXQocGFzdGUoIkMyIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzMiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzMiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDNCIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNCIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkM1IiksIGRhdGEgPSBnZXQocGFzdGUoIkM1IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzYiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzYiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICB5bGFiKHBhc3RlKCJBdmVyYWdlIiwgbW92ZW1lbnQpKSArCiAgICBsYWJzKGNvbG9yID0gIkNsdXN0ZXIiKSArCiAgICBnZ3RpdGxlKHBhc3RlKG1vdmVtZW50LCAiTW92ZW1lbnQgYmV0d2VlbiBDbHVzdGVycyIpKQogIGNsdXN0ZXIuYm94cGxvdCA8LSBnZ3Bsb3QoKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDMSIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMSIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzIiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzIiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkMzIiksIGRhdGEgPSBnZXQocGFzdGUoIkMzIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDNCIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNCIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzUiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzUiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkM2IiksIGRhdGEgPSBnZXQocGFzdGUoIkM2IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICB5bGFiKG1vdmVtZW50KSArCiAgICBsYWJzKGZpbGwgPSAiQ2x1c3RlciIpICsKICAgIGdndGl0bGUocGFzdGUobW92ZW1lbnQsICJCb3hwbG90IGJldHdlZW4gQ2x1c3RlcnMiKSkKICAgIAogIHByaW50KGNsdXN0ZXIucGxvdCkKICBwcmludChjbHVzdGVyLmJveHBsb3QpCn0KCmBgYAoKUG9zdC1DT1ZJRDE5CmBgYHtyfQoKCiMgdmlzdWFsaXplIGNvaW5zJyBwcmljZXMgd2l0aGluIGNsdXN0ZXIKcHJpY2VfY29scyA8LSBjKCJEYXRlIiwgIkNsb3NlIiwgIlZvbHVtZSIsICJjaGFuZ2UiLCAicmV0dXJucyIsICJ2b2xhdGlsaXR5IikKY2x1c3Rlci5ncm91cHMgPC0gYygiQzEiLCAiQzIiLCAiQzMiLCAiQzQiLCAiQzUiLCAiQzYiKQoKZm9yIChjbHVzdGVyLmdyb3VwIGluIGNsdXN0ZXIuZ3JvdXBzKSB7CiAgbmRheXMgPC0gMzA0CiAgY29pbnMucHJpY2VzIDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBuZGF5cywgbmNvbCA9IDApKQogIGNvaW5zLmNoYW5nZXMgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IG5kYXlzLCBuY29sID0gMCkpCiAgY29pbnMucmV0dXJucyA8LSBkYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbmRheXMsIG5jb2wgPSAwKSkKICBjb2lucy52b2xhdGlsaXR5IDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBuZGF5cywgbmNvbCA9IDApKQogIGZvciAoc3ltYm9sIGluIGdldChjbHVzdGVyLmdyb3VwKSRzeW1ib2wpIHsKICAgIHZhbHVlcyA8LSBmcmVhZChwYXN0ZSgiZGF0YXNldHMvZGFpbHkvY29pbnMiLCBwYXN0ZShzeW1ib2wsICdjc3YnLCBzZXAgPSAiLiIpLCBzZXAgPSAiLyIpKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIoRGF0ZSA+PSAiMjAyMS0wMS0wMSIpICU+JSBzZWxlY3QoYWxsX29mKHByaWNlX2NvbHMpKSAlPiUgYXJyYW5nZShEYXRlKQogICAgc3ltYm9sIDwtIHN0cl9zcGxpdChzeW1ib2wsICItIiwgc2ltcGxpZnkgPSBUKVsxXQogICAgCiAgICBjb2lucy5wcmljZXNbJ0RhdGUnXSA8LSB2YWx1ZXMkRGF0ZQogICAgY29pbnMuY2hhbmdlc1snRGF0ZSddIDwtIHZhbHVlcyREYXRlCiAgICBjb2lucy5yZXR1cm5zWydEYXRlJ10gPC0gdmFsdWVzJERhdGUKICAgIGNvaW5zLnZvbGF0aWxpdHlbJ0RhdGUnXSA8LSB2YWx1ZXMkRGF0ZQogICAgCiAgICBjb2lucy5wcmljZXNbc3ltYm9sXSA8LSB2YWx1ZXMkQ2xvc2UgCiAgICBjb2lucy5jaGFuZ2VzW3N5bWJvbF0gPC0gdmFsdWVzJGNoYW5nZQogICAgY29pbnMucmV0dXJuc1tzeW1ib2xdIDwtIHZhbHVlcyRyZXR1cm5zCiAgICBjb2lucy52b2xhdGlsaXR5W3N5bWJvbF0gPC0gdmFsdWVzJHZvbGF0aWxpdHkKICB9CiAgCiAgY29pbnMucHJpY2VzLm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMucHJpY2VzLCAiRGF0ZSIsIHZhbHVlLm5hbWUgPSAiUHJpY2VzIiwgdmFyaWFibGUubmFtZSA9ICJDb2lucyIpCiAgY29pbnMucHJpY2VzLnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5wcmljZXMubWVsdCwgYWVzKHggPSBEYXRlLCB5ID0gUHJpY2VzLCBjb2xvciA9IENvaW5zKSkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUHJpY2UgTW92ZW1lbnQgb2YiLCBjbHVzdGVyLmdyb3VwKSkgKwogICAgZ2VvbV9saW5lKCkKICBjb2lucy5wcmljZXMuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLnByaWNlcy5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gUHJpY2VzLCBmaWxsID0gQ29pbnMpKSArCiAgICBnZW9tX2JveHBsb3Qobm90Y2ggPSBUKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJQcmljZSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5wcmljZXMucGxvdCkKICBwcmludChjb2lucy5wcmljZXMuYm94cGxvdCkKICAgIAogIGNvaW5zLmNoYW5nZXMubWVsdCA8LSByZXNoYXBlMjo6bWVsdChjb2lucy5jaGFuZ2VzLCAiRGF0ZSIsIHZhbHVlLm5hbWUgPSAiQ2hhbmdlcyIsIHZhcmlhYmxlLm5hbWUgPSAiQ29pbnMiKQogIGNvaW5zLmNoYW5nZXMucGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLmNoYW5nZXMubWVsdCwgYWVzKHggPSBEYXRlLCB5ID0gQ2hhbmdlcywgY29sb3IgPSBDb2lucykpICsKICAgIGdndGl0bGUocGFzdGUoIkNoYW5nZSBNb3ZlbWVudCBvZiIsIGNsdXN0ZXIuZ3JvdXApKSArCiAgICBnZW9tX2xpbmUoKQogIGNvaW5zLmNoYW5nZXMuYm94cGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IGNvaW5zLmNoYW5nZXMubWVsdCwgYWVzKHggPSBDb2lucywgeSA9IENoYW5nZXMsIGZpbGwgPSBDb2lucykpICsKICAgIGdlb21fYm94cGxvdChub3RjaCA9IFQpICsKICAgIGdndGl0bGUocGFzdGUoIkNoYW5nZSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5jaGFuZ2VzLnBsb3QpCiAgcHJpbnQoY29pbnMuY2hhbmdlcy5ib3hwbG90KQoKICBjb2lucy5yZXR1cm5zLm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMucmV0dXJucywgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIlJldHVybnMiLCB2YXJpYWJsZS5uYW1lID0gIkNvaW5zIikKICBjb2lucy5yZXR1cm5zLnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy5yZXR1cm5zLm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IFJldHVybnMsIGNvbG9yID0gQ29pbnMpKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJSZXR1cm5zIE1vdmVtZW50IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIGdlb21fbGluZSgpCiAgY29pbnMucmV0dXJucy5ib3hwbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMucmV0dXJucy5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gUmV0dXJucywgZmlsbCA9IENvaW5zKSkgKwogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkgKwogICAgZ2d0aXRsZShwYXN0ZSgiUmV0dXJucyBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy5yZXR1cm5zLnBsb3QpCiAgcHJpbnQoY29pbnMucmV0dXJucy5ib3hwbG90KQoKICBjb2lucy52b2xhdGlsaXR5Lm1lbHQgPC0gcmVzaGFwZTI6Om1lbHQoY29pbnMudm9sYXRpbGl0eSwgIkRhdGUiLCB2YWx1ZS5uYW1lID0gIlZvbGF0aWxpdHkiLCB2YXJpYWJsZS5uYW1lID0gIkNvaW5zIikKICBjb2lucy52b2xhdGlsaXR5LnBsb3QgPC0gZ2dwbG90KGRhdGEgPSBjb2lucy52b2xhdGlsaXR5Lm1lbHQsIGFlcyh4ID0gRGF0ZSwgeSA9IFZvbGF0aWxpdHksIGNvbG9yID0gQ29pbnMpKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJWb2xhdGlsaXR5IE1vdmVtZW50IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIGdlb21fbGluZSgpCiAgY29pbnMudm9sYXRpbGl0eS5ib3hwbG90IDwtIGdncGxvdChkYXRhID0gY29pbnMudm9sYXRpbGl0eS5tZWx0LCBhZXMoeCA9IENvaW5zLCB5ID0gVm9sYXRpbGl0eSwgZmlsbCA9IENvaW5zKSkgKwogICAgZ2VvbV9ib3hwbG90KG5vdGNoID0gVCkgKwogICAgZ2d0aXRsZShwYXN0ZSgiVm9sYXRpbGl0eSBCb3hwbG90IG9mIiwgY2x1c3Rlci5ncm91cCkpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKICBwcmludChjb2lucy52b2xhdGlsaXR5LnBsb3QpIAogIHByaW50KGNvaW5zLnZvbGF0aWxpdHkuYm94cGxvdCkgCgogICMgYXZlcmFnZSBwcmljZXMgZm9yIGludGVyLWNsdXN0ZXIgY29tcGFyaXNvbgogIGNvaW5zLnByaWNlcy5hdmcgPC0gY29pbnMucHJpY2VzLm1lbHQgJT4lIGdyb3VwX2J5KERhdGUpICU+JSBzdW1tYXJpc2UoYXZnX3ByaWNlcyA9IG1lYW4oUHJpY2VzLCBuYS5ybSA9IFQpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyLmdyb3VwKQogIGNvaW5zLmNoYW5nZXMuYXZnIDwtIGNvaW5zLmNoYW5nZXMubWVsdCAlPiUgZ3JvdXBfYnkoRGF0ZSkgJT4lIHN1bW1hcmlzZShhdmdfY2hhbmdlcyA9IG1lYW4oQ2hhbmdlcywgbmEucm0gPSBUKSkgJT4lIG11dGF0ZShjbHVzdGVyID0gY2x1c3Rlci5ncm91cCkKICBjb2lucy5yZXR1cm5zLmF2ZyA8LSBjb2lucy5yZXR1cm5zLm1lbHQgJT4lIGdyb3VwX2J5KERhdGUpICU+JSBzdW1tYXJpc2UoYXZnX3JldHVybnMgPSBtZWFuKFJldHVybnMsIG5hLnJtID0gVCkpICU+JSBtdXRhdGUoY2x1c3RlciA9IGNsdXN0ZXIuZ3JvdXApCiAgY29pbnMudm9sYXRpbGl0eS5hdmcgPC0gY29pbnMudm9sYXRpbGl0eS5tZWx0ICU+JSBncm91cF9ieShEYXRlKSAlPiUgc3VtbWFyaXNlKGF2Z192b2xhdGlsaXR5ID0gbWVhbihWb2xhdGlsaXR5LCBuYS5ybSA9IFQpKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdGVyLmdyb3VwKQogIAogIGFzc2lnbihwYXN0ZShjbHVzdGVyLmdyb3VwLCAicHJpY2VzLmF2ZyIsIHNlcCA9ICIuIiksIGNvaW5zLnByaWNlcy5hdmcpCiAgYXNzaWduKHBhc3RlKGNsdXN0ZXIuZ3JvdXAsICJjaGFuZ2VzLmF2ZyIsIHNlcCA9ICIuIiksIGNvaW5zLmNoYW5nZXMuYXZnKQogIGFzc2lnbihwYXN0ZShjbHVzdGVyLmdyb3VwLCAicmV0dXJucy5hdmciLCBzZXAgPSAiLiIpLCBjb2lucy5yZXR1cm5zLmF2ZykKICBhc3NpZ24ocGFzdGUoY2x1c3Rlci5ncm91cCwgInZvbGF0aWxpdHkuYXZnIiwgc2VwID0gIi4iKSwgY29pbnMudm9sYXRpbGl0eS5hdmcpCn0KCiMgaW50ZXItY2x1c3RlciBwcmljZXMKZm9yIChtb3ZlbWVudCBpbiBjKCJQcmljZXMiLCAiQ2hhbmdlcyIsICJSZXR1cm5zIiwgIlZvbGF0aWxpdHkiKSkgewogIG1vdmVtZW50Lmxvd2VyIDwtIHRvbG93ZXIobW92ZW1lbnQpCiAgeV92YWwgPC0gcGFzdGUoImF2ZyIsIG1vdmVtZW50Lmxvd2VyLCBzZXAgPSAiXyIpCiAgY2x1c3Rlci5wbG90IDwtIGdncGxvdChkYXRhID0gZ2V0KHBhc3RlKCJjb2lucyIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIGFlcyh4ID0gRGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkMxIiksIGRhdGEgPSBnZXQocGFzdGUoIkMxIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzIiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzIiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDMyIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMyIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IGdldCh5X3ZhbCksIGNvbG9yID0gIkM0IiksIGRhdGEgPSBnZXQocGFzdGUoIkM0IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gZ2V0KHlfdmFsKSwgY29sb3IgPSAiQzUiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzUiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBnZXQoeV92YWwpLCBjb2xvciA9ICJDNiIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNiIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSkpICsKICAgIHlsYWIocGFzdGUoIkF2ZXJhZ2UiLCBtb3ZlbWVudCkpICsKICAgIGxhYnMoY29sb3IgPSAiQ2x1c3RlciIpICsKICAgIGdndGl0bGUocGFzdGUobW92ZW1lbnQsICJNb3ZlbWVudCBiZXR3ZWVuIENsdXN0ZXJzIikpCiAgY2x1c3Rlci5ib3hwbG90IDwtIGdncGxvdCgpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkMxIiksIGRhdGEgPSBnZXQocGFzdGUoIkMxIiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDMiIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDMiIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzMiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzMiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIGdlb21fYm94cGxvdChhZXMoeSA9IGdldCh5X3ZhbCksIHggPSBjbHVzdGVyLCBmaWxsID0gIkM0IiksIGRhdGEgPSBnZXQocGFzdGUoIkM0IiwgbW92ZW1lbnQubG93ZXIsICJhdmciLCBzZXAgPSAiLiIpKSwgbm90Y2ggPSBUKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHkgPSBnZXQoeV92YWwpLCB4ID0gY2x1c3RlciwgZmlsbCA9ICJDNSIpLCBkYXRhID0gZ2V0KHBhc3RlKCJDNSIsIG1vdmVtZW50Lmxvd2VyLCAiYXZnIiwgc2VwID0gIi4iKSksIG5vdGNoID0gVCkgKwogICAgZ2VvbV9ib3hwbG90KGFlcyh5ID0gZ2V0KHlfdmFsKSwgeCA9IGNsdXN0ZXIsIGZpbGwgPSAiQzYiKSwgZGF0YSA9IGdldChwYXN0ZSgiQzYiLCBtb3ZlbWVudC5sb3dlciwgImF2ZyIsIHNlcCA9ICIuIikpLCBub3RjaCA9IFQpICsKICAgIHlsYWIobW92ZW1lbnQpICsKICAgIGxhYnMoZmlsbCA9ICJDbHVzdGVyIikgKwogICAgZ2d0aXRsZShwYXN0ZShtb3ZlbWVudCwgIkJveHBsb3QgYmV0d2VlbiBDbHVzdGVycyIpKQogICAgCiAgcHJpbnQoY2x1c3Rlci5wbG90KQogIHByaW50KGNsdXN0ZXIuYm94cGxvdCkKfQoKYGBgCgo=